package com.innovation.ic.im.end.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.innovation.ic.b1b.framework.util.StringUtils;
import com.innovation.ic.im.end.base.pojo.constant.Constants;
import com.innovation.ic.im.end.base.pojo.constant.ContextParameter;
import com.innovation.ic.im.end.base.pojo.enums.ClientTypeEnum;
import com.innovation.ic.b1b.framework.util.HttpUtils;
import com.innovation.ic.im.end.base.pojo.global.Context;
import com.innovation.ic.im.end.base.value.config.ErpInterfaceAddressConfig;
import com.innovation.ic.im.end.base.value.config.FilterParamConfig;
import com.innovation.ic.im.end.base.value.config.RunEnvConfig;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 验证header中是否有Sec-WebSocket-Protocol(websocket请求)或extension(http请求)属性，其值为：{“Authorization”: clientId}
 */
@Component
public class AccessFilter implements GlobalFilter, Ordered {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private FilterParamConfig filterParamConfig;

    @Resource
    private ErpInterfaceAddressConfig erpInterfaceAddressConfig;

    @Resource
    private RunEnvConfig runEnvConfig;

    /**
    /**
     * 过滤规则：
     * 1.是否是允许通过的路径。
     * 2.header中是否有clientId。
     *
     * @param exchange
     * @param chain
     * @return
     */
    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        // 如果是允许直接通过了路径，则放行
        String path = request.getPath().toString();
        logger.info("调用接口:[{}]", path);
        List<String> allowPathList = filterParamConfig.getAllowPathList();
        for (int i = 0; i < allowPathList.size(); i++) {
            if (path.contains(allowPathList.get(i))) {
                logger.info("请求【" + path + "】中包含路径【" + allowPathList.get(i) + "】，直接放行");
                return chain.filter(exchange);
            }
        }

        // 设置headerName的默认值
        String headerName = Constants.HEADER_NAME;

        // 请求头中的验证参数
        String headerString = null;

        // websocket 请求中的验证参数
        String websocketHeaderName = filterParamConfig.getWebsocketHeaderName();
        String websocketHeaderNameStr = request.getHeaders().getFirst(websocketHeaderName);
        if(!StringUtils.isEmpty(websocketHeaderNameStr)){
            logger.info("-----当前请求方式为websocket-----");
            headerName = websocketHeaderName;
            headerString = websocketHeaderNameStr;
        }else{
            // http 请求中的验证参数
            String httpHeaderName = filterParamConfig.getHttpHeaderName();
            String httpHeaderNameStr = request.getHeaders().getFirst(httpHeaderName);
            if(!StringUtils.isEmpty(httpHeaderNameStr)){
                logger.info("-----当前请求方式为http-----");
                headerName = httpHeaderName;
                headerString = httpHeaderNameStr;
            }
        }

        logger.info(headerName + "的值:" + headerString);
        if (StringUtils.isEmpty(headerString)) {
            logger.warn("header中没有【" + headerName + "】属性");
            return null;
        } else {
            logger.info("header中" + headerName + "属性的值为【" + headerString + "】");

            // 从headerString中获取clientId
            String clientId = getClientIdFromHeaderString(headerString);
            if (StringUtils.isEmpty(clientId)) {
                logger.warn("Authorization属性不存在");
                return null;
            } else {
                logger.info("Authorization属性值为【" + clientId + "】");
                Set<String> clientSet = (Set<String>) Context.get(ContextParameter.CLIENT_SET);
                if (null == clientSet || clientSet.size() == 0) {
                    logger.warn("上下文中不存在【" + ContextParameter.CLIENT_SET + "】参数");
                    return null;
                } else {
                    Iterator<String> iterator = clientSet.iterator();
                    while (iterator.hasNext()) {
                        String theClientId = iterator.next();
                        // redis中存储的clientId为json格式的字符串,需要手动将clientId从json中获取出来
                        if(!StringUtils.isEmpty(theClientId)){
                            JSONObject json = JSON.parseObject(theClientId);
                            if(!json.isEmpty()){
                                theClientId = json.getString(Constants.ID);
                            }
                        }
                        if (clientId.equals(theClientId)) {
                            logger.info("clientId【" + clientId + "】合法，不对请求进行过滤");

                            // 判断token是否有效
                            Boolean result = judgeTokenIfEffective(request, clientId, clientSet);
                            if(!result){
                                logger.warn("Token不合法,请求被过滤掉了");
                                throw new Exception("登录状态已失效,请重新登录");
                            }

                            return chain.filter(exchange);
                        }
                    }
                    logger.warn("clientId【" + clientId + "】不合法，请求被过滤掉了");
                    return null;
                }
            }
        }
    }

    /**
     * 判断token是否有效
     * @param request 请求
     * @return 返回判断结果
     */
    private Boolean judgeTokenIfEffective(ServerHttpRequest request, String clientId, Set<String> clientSet){
        Boolean result = Boolean.FALSE;

        // 根据clientId获取客户端类型
        Integer typeById = null;
        Iterator<String> iterator = clientSet.iterator();
        while (iterator.hasNext()) {
            String theClientId = iterator.next();
            if(theClientId.contains(clientId)){
                JSONObject clientJson = JSONObject.parseObject(theClientId);
                typeById = Integer.valueOf((String) clientJson.get("type"));
            }
        }

        if(typeById != null && typeById.intValue() != ClientTypeEnum.ERP9.getCode()){
            logger.info("当前请求client为:[{}],不进行token校验", ClientTypeEnum.getDesc(typeById));
            return true;
        }

        String tokenName = filterParamConfig.getTokenName();
        String token = request.getHeaders().getFirst(tokenName);
        if(!Strings.isNullOrEmpty(token)){
            //logger.info("当前请求token为:[{}]", token);
            String s = HttpUtils.sendGet(erpInterfaceAddressConfig.getGetInfo(), null, token);
            logger.info(s);
            if (StringUtils.isEmpty(s)) {
                logger.error("调用接口异常,接口地址:[{}]", erpInterfaceAddressConfig.getGetInfo());
            }else{
                try {
                    JSONObject json = JSON.parseObject(s);
                    if(json != null && !json.isEmpty()){
                        Integer status = (Integer) json.get(Constants.STATUS_);
                        if(status.equals(HttpStatus.OK.value())){
                            logger.info("Token:[{}]有效,验证通过", token);
                            result = Boolean.TRUE;
                        }
                    }

                }catch (Exception e){
                    logger.error("调用接口异常,接口地址:[{}]", erpInterfaceAddressConfig.getGetInfo());
                }
            }
        }else{
            logger.info("当前请求token为空");
        }

        if(!result && !Constants.PROD_RUN_ENVIRONMENT.equals(runEnvConfig.getEnv())){
            logger.info("当前环境参数为:[{}],token无效时不拦截请求", runEnvConfig.getEnv());
            result = true;
        }

        return result;
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 从headerString中获取clientId
     * @param headerString 入参
     * @return 返回clientId
     */
    private String getClientIdFromHeaderString(String headerString){
        // 客户端id
        String clientId = null;

        // 判断headerString是否为json格式，区分取值方式
        Boolean result = isJSON(headerString);
        if(result){
            // 从json中获取clientId
            JSONObject jsonObject = JSONObject.parseObject(headerString);
            clientId = jsonObject.getString(filterParamConfig.getAuthorization());
        }else{
            /* 前端传来的验证参数需做特殊处理,数据需处理为K1=V1&K2=V2......格式再根据分隔符截取
               1.目前前端将等号替换为了单引号,需做替换;
               2.根据&截取K1=V1  K2=V2;
               3.根据=截取,获取参数名K和值V,并放到json中;
               4.根据字段authorization取到需要的值;
             */
            if(!StringUtils.isEmpty(headerString)){
                // 将前端传过来的单引号替换为等号供后续处理
                headerString = headerString.replaceAll("'", "=");
            }

            JSONObject jsonObject = new JSONObject();
            String[] params = headerString.split("&");
            for (String param : params) {
                String[] split = param.split("=");
                jsonObject.put(split[0], split[1]);
            }

            if(!jsonObject.isEmpty()){
                clientId = jsonObject.getString(filterParamConfig.getAuthorization());
            }
        }
        return clientId;
    }

    /**
     * 判断字符串是否为json
     * @param str 需要判断的字符串
     * @return 判断结果
     */
    public static Boolean isJSON(String str) {
        boolean result = false;
        try {
            Object obj=JSON.parse(str);
            result = true;
        } catch (Exception e) {
            result=false;
        }
        return result;
    }
}